package com.hanhai.zrb.api.biz.service.impl;

import java.util.ArrayList;
import java.util.List;

import javax.annotation.Resource;

import org.apache.commons.lang.ArrayUtils;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;

import com.hanhai.zrb.api.biz.dao.mybatis.FundMapper;
import com.hanhai.zrb.api.biz.dao.mybatis.IntentionMapper;
import com.hanhai.zrb.api.biz.dao.mybatis.ProjectMapper;
import com.hanhai.zrb.api.biz.service.ResourceMatchCacheManager;
import com.hanhai.zrb.api.biz.service.ResourceMatchServiceSolr;
import com.hanhai.zrb.api.solr.SolrHelper;
import com.hanhai.zrb.api.solr.SolrService;
import com.hanhai.zrb.model.fund.Fund;
import com.hanhai.zrb.model.intention.Intention;
import com.hanhai.zrb.model.intention.enums.IntentionTypeEnum;
import com.hanhai.zrb.model.project.Project;
import com.hanhai.zrb.model.project.ProjectSolrDto;
import com.hanhai.zrb.model.project.enums.ProjectStatusEnums;
import com.hanhai.zrb.model.resourcematch.ResourceMatchContent;
import com.hanhai.zrb.model.resourcematch.ResourceMatchLevelEnum;
import com.hanhai.zrb.model.resourcematch.ResourceMatchCondition;
import com.hanhai.zrb.model.resourcematch.ResourceMatchResult;
import com.hanhai.zrb.model.resourcematch.ResourceMatchTypeEnum;

//匹配用Solr查询，规则1、规则2、规则3
//匹配结果存在Redis,id,type,ids(1,2,3)匹配结果
//发布，直接删除缓存
//缓存为7天
//查询匹配，实时去查询，包装分页

//第1步：传入id和type
//第2步：从Mysql查询行业、地区、资金3项信息
//第3步：从Solr查询（匹配：比较3项），已发布状态的项目和资金
//规则1匹配：存入Redis，缓存时间为7天
//规则2匹配：存入Redis
//规则3匹配：存入Redis
//第4步：查询
//单个查询，查询不到，就匹配。分页、排序（时间、优先级）

//Redis存入
//id:type:level1 bizid(1,2,3,4) 从Solr查询出来的，按照（时间排序了）
//id:type:level2 bizid(333)
//id:type:level3 bizid(22)

//匹配前3位，“查看单个意向的所有匹配信息”
//把3个level的bizid查询出来，放到List中，再按照分页或者条数，查询？

//其它：意向修改、项目发布、资金发布（删除存储，等待下次查询）；
//更多包装接口，需要在Redis包装分页等查询之后，再查询关联数据（李诚用）

//疑问难点
//1：Solr查询，地区和行业，多个字段怎么考虑？
//a.都包含全国，“地区一致（含全国）”，意味着，（全国），（武汉）不匹配？
//b.（北京，武汉），（武汉，南京），匹配？ Solr怎么模糊匹配，或者“,#解析”，Solr精确匹配？
//3.匹配了，但是数据为0（这个时候不会更新存储）。什么状态？
//4.过期时间，默认为7天，如果后台设置了，新存入的数据，使用新的过期时间？
//5.不再需要定时器了。
//6.是否需要 提前自动匹配的接口。原来的可能会发生“实施匹配”。
@Service
public class ResourceMatchServiceSolrImpl implements ResourceMatchServiceSolr {

	@Resource
	private IntentionMapper intentionMapper;
	@Resource
	private ProjectMapper projectMapper;
	@Resource
	private FundMapper fundMapper;
	@Resource
	private ResourceMatchCacheManager resourceMatchCacheManager;

	private Logger logger = Logger.getLogger(getClass());

	@Resource
	private SolrService solrService;

	/** 默认开启缓存，优先从Redis中获得匹配数据 */
	public ResourceMatchResult match(Integer type, Long bizid) {
		return match(type, bizid, true, ResourceMatchLevelEnum.ALL);
	}

	/**
	 * @param type
	 *            项目意向，资金意向,项目，资金
	 * @param bizid
	 *            业务id，意向、资金、项目
	 * @return 所有的匹配结果集
	 */
	public ResourceMatchResult match(Integer type, Long bizid, boolean cache,
			ResourceMatchLevelEnum level) {
		logger.info("Try to match resource ~ type=" + type + ",id=" + bizid);
		if (type == null || bizid == null) {
			logger.error("The param type or id is null~type=" + type + ",id="
					+ bizid);
			return null;
		}
		if (!ResourceMatchTypeEnum.exist(type)) {
			logger.error("The type is illegal~type=" + type);
			return null;
		}
		ResourceMatchResult resourceMatchResult = new ResourceMatchResult();
		List<ResourceMatchContent> list = new ArrayList<ResourceMatchContent>();
		// 意向
		boolean isIntentionProject = type
				.equals(ResourceMatchTypeEnum.INTENTION_PROJECT.getCode());
		boolean isIntentionFund = type
				.equals(ResourceMatchTypeEnum.INTENTION_FUND.getCode());
		boolean isIntention = isIntentionProject || isIntentionFund;
		ResourceMatchCondition matchResource = new ResourceMatchCondition();
		matchResource.setBizid(bizid);
		matchResource.setType(type);
		matchResource.setCache(cache);
		matchResource.setLevel(level);
		// 意向
		if (isIntention) {
			Intention intention = intentionMapper.selectByPrimaryKey(bizid);
			if (intention == null) {
				logger.error("The intention is null,id=" + bizid);
				return null;
			}
			Integer intentionType = intention.getType();
			fillMatchResourceWithIntention(matchResource, intention);
			checkMatchResource(matchResource);
			if (isIntentionProject) {
				// 检查意向的类型是否正确
				if (!intentionType.equals(IntentionTypeEnum.project.getCode())) {
					throw new RuntimeException(
							"Intention Type Not Match With project~");
				}
				logger.info("matchProjectWithFund,intention-project");
				list = matchProjectWithFund(matchResource);
			} else if (isIntentionFund) {
				// 检查意向的类型是否正确
				if (!intentionType.equals(IntentionTypeEnum.fund.getCode())) {
					throw new RuntimeException(
							"Intention Type Not Match With fund~");
				}
				logger.info("matchFundWithProject,intention-fund");
				list = matchFundWithProject(matchResource);
			}
		}
		// 项目
		else if (type.equals(ResourceMatchTypeEnum.PROJECT.getCode())) {
			Project project = projectMapper.selectByPrimaryKey(bizid);
			if (project == null) {
				logger.error("The project is null,bizid=" + bizid);
				return null;
			}
			fillMatchResourceWithProject(matchResource, project);
			checkMatchResource(matchResource);
			logger.info("matchProjectWithFund,project");
			list = matchProjectWithFund(matchResource);
		}
		// 资金
		else if (type.equals(ResourceMatchTypeEnum.FUND.getCode())) {
			Fund fund = fundMapper.findById(bizid);
			if (fund == null) {
				logger.error("The fund is null,bizid=" + bizid);
				return null;
			}
			fillMatchResourceWithFund(matchResource, fund);
			checkMatchResource(matchResource);
			logger.info("matchFundWithProject,fund");
			list = matchFundWithProject(matchResource);
		}
		resourceMatchResult.setResourceMatchContentList(list);
		resourceMatchResult.setResourceMatchResource(matchResource);
		return resourceMatchResult;
	}

	/**
	 * 行业、地区、资金，intention->matchResource
	 */
	private void fillMatchResourceWithIntention(
			ResourceMatchCondition matchResource, Intention intention) {
		matchResource.setAmount(intention.getAmount());
		matchResource.setDistrict(intention.getDistrict());
		matchResource.setIndustry(intention.getIndustry());
	}

	/**
	 * 行业、地区、资金，fund->matchResource
	 */
	private void fillMatchResourceWithFund(ResourceMatchCondition matchResource,
			Fund fund) {
		matchResource.setAmount(fund.getTotal());
		matchResource.setDistrict(fund.getDestination());
		matchResource.setIndustry(fund.getIndustryId());
	}

	/**
	 * 行业、地区、资金，project->matchResource
	 */
	private void fillMatchResourceWithProject(
			ResourceMatchCondition matchResource, Project project) {
		matchResource.setAmount(project.getTotal().doubleValue());
		matchResource.setDistrict(project.getProvincePostcode());
		matchResource.setIndustry(project.getIndustryId() + "");
	}

	private List<ResourceMatchContent> matchProjectWithFund(
			ResourceMatchCondition matchResource) {
		List<ResourceMatchContent> list = null;
		String key = ResourceMatchCacheManagerImpl.buildKey(matchResource);
		List<ResourceMatchContent> listInRedis = null;
		if (matchResource.isCache()) {
			listInRedis = resourceMatchCacheManager.get(key);
		}
		if (listInRedis != null) {
			list = listInRedis;
			logger.info("Find fund4Project in redis~ size=" + list.size());
		} else {
			list = doMatchProjectWithFund(matchResource);
			resourceMatchCacheManager.add(key, list);
		}
		return list;
	}

	private List<ResourceMatchContent> doMatchProjectWithFund(
			ResourceMatchCondition matchResource) {
		List<ResourceMatchContent> list = new ArrayList<ResourceMatchContent>();
		ResourceMatchLevelEnum level = matchResource.getLevel();
		if (level.equals(ResourceMatchLevelEnum.ALL)
				|| level.equals(ResourceMatchLevelEnum.FIRST)) {
			List<Fund> firstLevelFundList = queryFirstLevelFund4Project(matchResource);
			fillMatchContentList4Project(list,
					ResourceMatchLevelEnum.FIRST.getCode(), firstLevelFundList);
		}
		if (level.equals(ResourceMatchLevelEnum.ALL)
				|| level.equals(ResourceMatchLevelEnum.SECOND)) {
			List<Fund> secondLevelFundList = querySecondLevelFund4Project(matchResource);
			fillMatchContentList4Project(list,
					ResourceMatchLevelEnum.SECOND.getCode(),
					secondLevelFundList);
		}
		if (level.equals(ResourceMatchLevelEnum.ALL)
				|| level.equals(ResourceMatchLevelEnum.THIRD)) {
			List<Fund> thirdLevelFundList = queryThirdLevelFund4Project(matchResource);
			fillMatchContentList4Project(list,
					ResourceMatchLevelEnum.THIRD.getCode(), thirdLevelFundList);
		}
		return list;
	}

	// 先从Redis缓存中取，如果有，直接使用；如果没有，实时匹配，再存入Redis，再使用。
	private List<ResourceMatchContent> matchFundWithProject(
			ResourceMatchCondition matchResource) {
		List<ResourceMatchContent> list = null;
		String key = ResourceMatchCacheManagerImpl.buildKey(matchResource);
		List<ResourceMatchContent> listInRedis = null;
		if (matchResource.isCache()) {
			listInRedis = resourceMatchCacheManager.get(key);
		}
		if (listInRedis != null) {
			list = listInRedis;
			logger.info("Find project4Fund in redis~ size=" + list.size());
		} else {
			list = doMatchFundWithProject(matchResource);
			resourceMatchCacheManager.add(key, list);
		}
		return list;
	}

	// 从Solr中匹配，再返回
	private List<ResourceMatchContent> doMatchFundWithProject(
			ResourceMatchCondition matchResource) {
		List<ResourceMatchContent> list = new ArrayList<ResourceMatchContent>();
				
		ResourceMatchLevelEnum level = matchResource.getLevel();
		if (level.equals(ResourceMatchLevelEnum.ALL)
				|| level.equals(ResourceMatchLevelEnum.FIRST)) {
			List<ProjectSolrDto> firstLevelProjectList = queryFirstLevelProject4Fund(matchResource);
			fillMatchContentList4Fund(list, ResourceMatchLevelEnum.FIRST.getCode(),
					firstLevelProjectList);
		}
		if (level.equals(ResourceMatchLevelEnum.ALL)
				|| level.equals(ResourceMatchLevelEnum.SECOND)) {
			List<ProjectSolrDto> secondLevelProjectList = querySecondLevelProject4Fund(matchResource);
			fillMatchContentList4Fund(list,
					ResourceMatchLevelEnum.SECOND.getCode(), secondLevelProjectList);
		}
		if (level.equals(ResourceMatchLevelEnum.ALL)
				|| level.equals(ResourceMatchLevelEnum.THIRD)) {
			List<ProjectSolrDto> thirdLevelProjectList = queryThirdLevelProject4Fund(matchResource);
			fillMatchContentList4Fund(list, ResourceMatchLevelEnum.THIRD.getCode(),
					thirdLevelProjectList);
		}			
	
		return list;
	}

	private void fillMatchContentList4Fund(List<ResourceMatchContent> list,
			Integer level, List<ProjectSolrDto> projectList) {
		if (projectList != null) {
			for (ProjectSolrDto project : projectList) {
				ResourceMatchContent content = new ResourceMatchContent();
				content.setLevel(level);
				content.setBizid(project.getId());
				list.add(content);
			}
		}

	}

	private List<ProjectSolrDto> queryFirstLevelProject4Fund(
			ResourceMatchCondition matchResource) {	
		SolrHelper<ProjectSolrDto> solrHelper =industryDistrictSorlHepler4Fund(matchResource);
		Double amount = matchResource.getAmount();
		Double start = amount - THOUSAND;
		Double end = amount + THOUSAND;
		solrHelper.andBetween(TOTAL, start + "", end + "");
		List<ProjectSolrDto> firstLevelProjectList = doQueryProject(solrHelper);
		return firstLevelProjectList;
	}
	private List<ProjectSolrDto> doQueryProject(SolrHelper<ProjectSolrDto> solrHelper) {
		// 已发布
		solrHelper.andEquals(PROJECT_STATUS, ProjectStatusEnums.RELEASE.getCode() + "");
		List<ProjectSolrDto> projectList = solrHelper.query(PROJECT_SORT, ProjectSolrDto.class);
		return projectList;
	}
	
	private SolrHelper<ProjectSolrDto> industryDistrictSorlHepler4Fund(ResourceMatchCondition matchResource){	
		SolrHelper<ProjectSolrDto> solrHelper = industrySolrHelper4Fund(matchResource);		
		//资金存在“全国”
		String district = matchResource.getDistrict();
		String[] provincePostcodeArray=district.split(SPLIT_REGEX);
		boolean existAllProvincePostcode =ArrayUtils.contains(provincePostcodeArray, ALL_PROVINCE_POSTCODE);
		if(!existAllProvincePostcode){
			solrHelper.andIn(PROVINCE_POSTCODE, provincePostcodeArray);
		}else{
			logger.info("queryFirstLevelProject4Fund 资金是全部地区");
		}
		return solrHelper;
	}
	private List<ProjectSolrDto> querySecondLevelProject4Fund(
			ResourceMatchCondition matchResource) {
		SolrHelper<ProjectSolrDto> solrHelper =industryDistrictSorlHepler4Fund(matchResource);
		Double amount = matchResource.getAmount();
		Double start = amount - THOUSAND;
		Double end = amount + THOUSAND;
		solrHelper.andNotBetween(TOTAL, start + "", end + "");
		List<ProjectSolrDto> firstLevelProjectList = doQueryProject(solrHelper);
		return firstLevelProjectList;
	}

	private List<ProjectSolrDto> queryThirdLevelProject4Fund(
			ResourceMatchCondition matchResource) {
		SolrHelper<ProjectSolrDto> solrHelper = industrySolrHelper4Fund(matchResource);
		//资金存在“全国”
		String district = matchResource.getDistrict();
		String[] provincePostcodeArray=district.split(SPLIT_REGEX);
		boolean existAllProvincePostcode =ArrayUtils.contains(provincePostcodeArray, ALL_PROVINCE_POSTCODE);
		if(existAllProvincePostcode){
			//第3Level，如果存在“全国”，地区一定满足，不符合
			logger.info("queryThirdLevelProject4Fund 资金是全部地区");
			return null;		
		}else{
			solrHelper.andNotIn(PROVINCE_POSTCODE, provincePostcodeArray);
		}
	
		List<ProjectSolrDto> thirdLevelProjectList = doQueryProject(solrHelper);
		return thirdLevelProjectList;
	}

	private SolrHelper<ProjectSolrDto> industrySolrHelper4Fund(
			ResourceMatchCondition matchResource) {
		SolrHelper<ProjectSolrDto> solrHelper = new SolrHelper<ProjectSolrDto>(
				solrService.getSolrProjectUrl());
		String industry = matchResource.getIndustry();
		String[] industryArray=industry.split(SPLIT_REGEX);
		//资金存在全部行业，行业已经匹配了
		boolean existAllIndustry = ArrayUtils.contains(industryArray, ALL_INDUSTRY);
	
		if(!existAllIndustry){
			solrHelper.andIn(INDUSTRY_ID, industryArray);
		}else{
			logger.info("queryThirdLevelProject4Fund 资金是全部行业");
		}
		return solrHelper;
	}

	private void fillMatchContentList4Project(List<ResourceMatchContent> list,
			Integer level, List<Fund> firstLevelFundList) {
		if (firstLevelFundList != null) {
			for (Fund fund : firstLevelFundList) {
				ResourceMatchContent content = new ResourceMatchContent();
				content.setLevel(level);
				content.setBizid(fund.getId());
				list.add(content);
			}
		}
	}

	// 几个常量
	private static final String FUND_SORT = "operatorTime desc";
	private static String PROJECT_SORT = "pushTime desc";
	private static String PROVINCE_POSTCODE="provincePostcode";
	private static String SPLIT_REGEX=",|#";
	// 数据库的单位是（万）
	private static final int THOUSAND = 1000;

	private static final String TOTAL = "total";
	private static final String DESTINATION = "destination";
	private static final String INDUSTRY_ID = "industryId";
	private static final String STATE = "state";
	private static String ALL_INDUSTRY="97";
	private static String ALL_PROVINCE_POSTCODE="0";
	private static final String PROJECT_STATUS = "projectStatus";
	// 第一优先级：满足行业一致、地区一致（含全国）、金额相差1000万内
	private List<Fund> queryFirstLevelFund4Project(
			ResourceMatchCondition matchResource) {
		SolrHelper<Fund> solrHelper = industryDistrictSorlHepler4Project(matchResource);
		Double amount = matchResource.getAmount();
		Double start = amount - THOUSAND;
		Double end = amount + THOUSAND;
		solrHelper.andBetween(TOTAL, start + "", end + "");

		List<Fund> firstLevelFundList = doQueryFund(solrHelper);
		return firstLevelFundList;
	}

	private SolrHelper<Fund> industryDistrictSorlHepler4Project(
			ResourceMatchCondition matchResource) {
		SolrHelper<Fund> solrHelper = industrySorlHepler4Project(matchResource);
		solrHelper.andStartSub();
		solrHelper.andLike(DESTINATION, matchResource.getDistrict());
		//选择全国的时候，不能选择其它地区
		solrHelper.orEquals(DESTINATION, ALL_PROVINCE_POSTCODE);
		solrHelper.endSub();
		return solrHelper;
	}

	private SolrHelper<Fund> industrySorlHepler4Project(
			ResourceMatchCondition matchResource) {
		SolrHelper<Fund> solrHelper = new SolrHelper<Fund>(
				solrService.getSolrFundUrl());
		solrHelper.andStartSub();
		solrHelper.orLike(INDUSTRY_ID, matchResource.getIndustry());
		//选择全部行业的时候，不能选择其它行业
		solrHelper.orEquals(INDUSTRY_ID, ALL_INDUSTRY);
		solrHelper.endSub();
		return solrHelper;
	}

	// 第二优先级：满足行业一致、地区一致（含全国）、金额不限
	// 为防止重复，又不想在查询的List中剔除和Level-1重复的，这里限定金额
	private List<Fund> querySecondLevelFund4Project(
			ResourceMatchCondition matchResource) {
		SolrHelper<Fund> solrHelper = industryDistrictSorlHepler4Project(matchResource);
		Double amount = matchResource.getAmount();
		Double start = amount - THOUSAND;
		Double end = amount + THOUSAND;
		solrHelper.andNotBetween(TOTAL, start + "", end + "");
		List<Fund> secondLevelFundList = doQueryFund(solrHelper);
		return secondLevelFundList;
	}

	// 第三优先级：满足行业一致、地区不一致（含全国）、金额不限
	private List<Fund> queryThirdLevelFund4Project(
			ResourceMatchCondition matchResource) {
		SolrHelper<Fund> solrHelper = industrySorlHepler4Project(matchResource);
		solrHelper.andNotLike(DESTINATION, matchResource.getDistrict());
		// 查询所有，不分页
		List<Fund> thirdLevelFundList = doQueryFund(solrHelper);
		return thirdLevelFundList;
	}

	private List<Fund> doQueryFund(SolrHelper<Fund> solrHelper) {
		// 已发布
		solrHelper.andEquals(STATE, ProjectStatusEnums.RELEASE.getCode() + "");
		List<Fund> fundList = solrHelper.query(FUND_SORT, Fund.class);
		return fundList;
	}

	/**
	 * 检查3个匹配参数: 行业、地区、资金
	 */
	private void checkMatchResource(ResourceMatchCondition matchResource) {
		double defaultAmount = -10000;
		String defaultDistrict = "";
		String defaultIndustry = "";
		logger.info("The matchResource is : " + matchResource);
		if (matchResource.getAmount() == null) {
			logger.error("The amount is null,set to default -10000 ");
			matchResource.setAmount(defaultAmount);
		}
		if (matchResource.getDistrict() == null) {
			logger.error("The district is null~");
			matchResource.setDistrict(defaultDistrict);
		}
		if (matchResource.getIndustry() == null) {
			logger.error("The industry is null~");
			matchResource.setIndustry(defaultIndustry);
		}
	}

}
